#pragma strict

var type : int; //2 random object, 1 zombie, 0 human
var closestPOI : GameObject;
var dir : Vector3;
var nextWaypointDistance : float = 3;

var panic : int= 0;
var speed : float = 1;
var dmg : int = 5;
var hp : int = 10;
var infected : boolean = false;
var curse : int = 0; 
var courage : int = 20;
var weapon : GameObject;
var saferoomlist : GameObject[];

var behaviour;

private var panicked : boolean = false;
 var path : Pathfinding.Path;
 var pathstorage : Pathfinding.Path;
private var currentWaypoint : int = 0;
private var controller : CharacterController; 
private var seeker : Seeker;
private var animationman : Animation;
private var stopmoving : boolean = false;
//TODO: Optimize as much as possible
//TODO: Find way to avoid humans and zombies blocking eachother. look into Passive "pushaway" model like in tf2?

function Start ()
{
	seeker = GetComponent (Seeker);
	controller = GetComponent (CharacterController);
	animationman = GetComponentInChildren(Animation);
	saferoomlist = GameObject.FindGameObjectsWithTag("Room");
	standAnim();
}
 
function restorePath()
{
	path = pathstorage;
	pathstorage = null;
}
///////////////////////////////////////////////////////////////////////////////////////////////////////// 
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// FINDING FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////// 

function FindClosest (tag) : GameObject 
{
    // Find all game objects with tag
    var gos : GameObject[];
    gos = GameObject.FindGameObjectsWithTag(tag); 
    var closest : GameObject; 
    var distance = Mathf.Infinity; 
    var position = transform.position; 
    // Iterate through them and find the closest one
    for (var go : GameObject in gos)  
    { 
        var diff = (go.transform.position - position);
        var curDistance = diff.sqrMagnitude; 
        if (curDistance < distance) 
        { 
            closest = go; 
            distance = curDistance; 	
        } 
    } 
    return closest;    
}

function FindRandom(tag) : GameObject 
{
    // Find all game objects with tag
    var gos : GameObject[];
    gos = GameObject.FindGameObjectsWithTag(tag); 
    var random : GameObject;
    random = gos[Mathf.Round(Random.Range(0,gos.length))];
    return random;    
}

function FindRandomInList(list : Array) 
{
    return list[Mathf.Round(Random.Range(0,list.length))];    
} 

function FindClosestVisible (tag) : GameObject 
{
    // Find all game objects with tag
    var bgos = new Array (GameObject.FindGameObjectsWithTag(tag));  
    var closest : GameObject; 
    var distance = Mathf.Infinity; 
    var position = transform.position;
    var go : GameObject;  
    var hit : RaycastHit;
    // Iterate through them and check them all if there's something between
    for (var i:int=0;i<bgos.length;i++)  
    {  
   		go=bgos[i];  
  		Physics.Linecast (position, go.transform.position, hit);	
	    if (hit.transform!=go.transform) 
	    {
	    	bgos.RemoveAt(i);
	    	i-=1;
    	}
    }  
    for (i=0;i<bgos.length;i++)  
    { 
	   	go=bgos[i]; 
        var diff = (go.transform.position - position);
        var curDistance = diff.sqrMagnitude; 
        if (curDistance < distance) 
        { 
            closest = go; 
            distance = curDistance; 
        } 
    } 
    return closest;    
}

function FindVisible (tag) : Array
{
    // Find all game objects with tag
    var bgos = new Array (GameObject.FindGameObjectsWithTag(tag));  
    var hit : RaycastHit;
    var go : GameObject;  
    // Iterate through them and check them all if there's something between
    for (var i:int=0;i<bgos.length;i++)  
    {  
   		go=bgos[i];  
  		Physics.Linecast (transform.position, go.transform.position, hit);	
	    if (hit.transform!=go.transform) 
	    {
	    	bgos.RemoveAt(i);
	    	i-=1;
    	}
    }  
    return bgos;    
}
///////////////////////////////////////////////////////////////////////////////////////////////////////// 
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// ANIMATION FUNCTIONS
/////////////////////////////////////////////////////////////////////////////////////////////////////////
function walkAnim()
{
	var animname;
	if(type == 0)
	{
		if(speed >= 10)
		{
			if(panic >= courage) animname = "h_run_panic";
			else animname = "h_run";
		}
		else animname = "h_walk";
	}
	else if(type == 1)
	{
		animname = "z_shuffle";
	}
	
	if(!animationman.IsPlaying(animname)) animationman.CrossFade(animname);
}

function standAnim()
{
	var animname;
	if(type == 0)
	{
		animname = "h_stand";
	}
	else if(type == 1)
	{
		animname = "z_stand";
	}
	
	if(!animationman.IsPlaying(animname)) animationman.CrossFade(animname);
}

function attackAnim()
{
	var animname; 
	if(type == 0)
	{
		animname = "h_shoot";
	}
	else if(type == 1) 
	{ 
		animname = "z_attack1";
		if(!IsInvoking("applyDamage")&&!IsInvoking("pathRestore")) Invoke("applyDamage",0.79167);
		if(!IsInvoking("pathRestore")) Invoke("pathRestore",1.58);
	}
	if(!animationman.IsPlaying(animname)) animationman.CrossFade(animname);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
// AI FUNCTIONS
///////////////////////////////////////////////////////////////////////////////////////////////////////// 

function followPath()
{
	if (Vector3.Distance (transform.position,path.vectorPath[currentWaypoint]) < nextWaypointDistance) 
	{					
    	if (currentWaypoint+1 >= path.vectorPath.Length) 
		{
    		Debug.Log ("End Of Path Reached");
    		currentWaypoint = 0;
    		path = null;
    		standAnim();
 			return true;
    	}
    	else currentWaypoint += 1;
    }
	dir = (path.vectorPath[currentWaypoint]-transform.position).normalized;
	dir *= speed * Time.fixedDeltaTime;
	
	Debug.DrawLine(transform.position, transform.position+dir, Color.magenta);
	Debug.DrawLine(transform.position, path.endPoint, Color.white);
	
	controller.Move(dir);
	
	walkAnim();
	
	transform.LookAt(transform.position+dir);
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
function determineFleePath(closPOI : GameObject)
{ 		//TODO: Optimize & improve this!
	var escapepoint = transform.position - closPOI.transform.position;
	escapepoint.y = 0;
	
	escapepoint = Vector3.Normalize(escapepoint);
	
	escapepoint*=10;
	
	Debug.DrawLine(transform.position,transform.position+escapepoint, Color.blue);
	
	var hit : RaycastHit;
	var mask : LayerMask = 8;
	
	Physics.Linecast (transform.position,transform.position+escapepoint, hit);
		
	if (hit.transform) //there's something in the way!
	{ 
		Physics.Linecast (transform.position,transform.position+escapepoint, hit);
		
		Debug.DrawLine(hit.point,hit.point+hit.normal, Color.red);
		
		var pervec1 : Vector3; 	var pervec2 : Vector3; 
		var ray1 : Ray; 		var ray2 : Ray;
		var hit1 : RaycastHit; 	var hit2: RaycastHit;
		
		pervec1.x = hit.normal.z; pervec1.z = -hit.normal.x;
		pervec2.x = -hit.normal.z; pervec2.z = hit.normal.x; 
		
		ray1.origin=hit.point+hit.normal; ray1.direction = pervec1;
		ray2.origin=hit.point+hit.normal; ray2.direction = pervec2;
		
		Physics.Raycast(ray1,hit1,Mathf.Infinity);
		Physics.Raycast(ray2,hit2,Mathf.Infinity);
		
		Debug.DrawLine(hit.point+hit.normal,hit1.point, Color.yellow);
		Debug.DrawLine(hit.point+hit.normal,hit2.point, Color.yellow);
		
		if(hit1.distance>hit2.distance)
		{
			escapepoint = ray1.GetPoint(10);
		}
		else
		{
			escapepoint = ray2.GetPoint(10);
		}
    }
    else 
    {
    	escapepoint+=transform.position;
    } 
    return escapepoint;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
function applyDamage()
{
	var hit : RaycastHit; //Hand to hand!
	Debug.DrawLine(transform.position,transform.position+transform.TransformDirection(Vector3.forward*3), Color.black);
	if(Physics.Raycast(transform.position,transform.TransformDirection(Vector3.forward), hit,3))
	{
		var aiscript = hit.transform.GetComponent(AI_script);
		if(aiscript)
		{
			aiscript.hp -= dmg;
			aiscript.panic += dmg;
			if(type==1) aiscript.infected = true;
		}
	}
}

function Attack()
{  
	if(weapon) //If we do have a weapon, use it
	{
		weapon.GetComponent(wep_script).activate();
	}
	else 
	{
		if(!IsInvoking("pathRestore"))
		{ 
			pathstorage = path; path = null; 
			Invoke("pathRestore",2);
		}
		if(!IsInvoking("attackAnim"))
		{
			Invoke("applyDamage",0.79167);
			Invoke("attackAnim",2);
		}
	}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
function findRoomandstartpath()
{
	var targetroom : GameObject = FindRandomInList(saferoomlist);
	if(targetroom)
	{
		var randomvector : Vector3 = Vector3(Random.value,0,Random.value);
		var targetpos : Vector3 = targetroom.transform.position-targetroom.transform.localScale/2+Vector3.Scale(targetroom.transform.localScale,randomvector);
		seeker.StartPath (transform.position,targetpos,OnPathComplete);
	}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
		//Type 1 - HUMAN
		
		//If we see a zombie in the room, remove the room from the saferoom list
		//If we see a zombie, raise panic level by 1
		//if we see a zombie, we have a weapon and our panic level is less than half our courage, we attack the zombie
		//if we see a zombie, we have a weapon and our panic level is more than half our courage, we run away
		//if we see a zombie, we don't have a weapon and our panic level is less than half our courage, we try to get to the nearest weapon
		//if we see a zombie,  we don't have a weapon and our panic level is less than half our courage, we try to get to the nearest weapon
	
		//If we see a person killed, raise panic level by 5
		//If we hear a person killed, raise panic level by 3
		//If we see a zombie killed, diminish panic level by 3
		
		//If we don't see a zombie, and our panic level is less than half our courage, we look for a random saferoom room 
		//If we don't see a zombie, and our panic level is more than half our courage, we stay in our room 
		//If we don't see a zombie, and our panic level is more than our courage, we panic and run around randomly 

function DetermineHumanBehaviour()
{
	closestPOI=FindClosestVisible("Zombie");
	if(closestPOI)
	{
		 if(weapon)
		 {
		 	
		 }
		 else if(panic<courage/2)
		 { 
		 	
		 } 
		 else if(panic>courage/2&&panic<courage)
		 { 
		 	
		 }	
		 else if(panic>courage)
		 {
		 	if(!path && !IsInvoking("panic")) Invoke("panic", 1	+Random.Range(1,3));
		 }
	}
	else 
	{
		if(panic<courage/2)
		{
			if(!path && !IsInvoking("findRoomandstartpath")) Invoke("findRoomandstartpath", 2+Random.Range(1,3));	
		}
		else if(panic>courage/2&&panic<courage)
		{
		
		}
		else if(panic>courage)
		{
			if(!path && !IsInvoking("panic")) Invoke("panic", 1	+Random.Range(1,3));
		}
	}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////// 
function ZombieFindTarget()
{
	var Bacon=FindClosest("Bacon"); closestPOI=FindClosest("Human"); 
	
	if(Bacon)
	{
		if(closestPOI)
		{
			if(Vector3.Distance(Bacon.transform.position, transform.position)<50&&Vector3.Distance(closestPOI.transform.position, transform.position)>30)
			{
				closestPOI=Bacon;
			}
			else if(Vector3.Distance(closestPOI.transform.position, transform.position)>30)
			{
				closestPOI=null;
			}
		}
		else closestPOI=Bacon;
	}
	else if(Vector3.Distance(closestPOI.transform.position, transform.position)>30)
	{
		 closestPOI=null;
	}			
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
function ZombieRepath()
{
	if(closestPOI)
	{ 
		if(pathstorage) return;
		
		if(path)
		{
			var deltadistPOIpath : Vector3 = path.endPoint-closestPOI.transform.position;
			if(deltadistPOIpath.magnitude>5)
			{
				 seeker.StartPath (transform.position,closestPOI.transform.position,OnPathComplete);
			}
		}
		else
		{
			seeker.StartPath (transform.position,closestPOI.transform.position,OnPathComplete);
		}
	}
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
function DetermineZombieBehaviour()
{
	if(!IsInvoking("ZombieFindTarget")) Invoke("ZombieFindTarget", Random.Range(1,3));	

	if(closestPOI)
	{		
		if(Physics.Linecast (transform.position,closestPOI.transform.position))
		{   
			if(!IsInvoking("ZombieRepath")||!IsInvoking("Attack")) 
			{
				//TODO: Determine if human is close enought. If so, ATTACK! 
				if(Vector3.Distance(transform.position,closestPOI.transform.position)<3)
				{
					Attack();
				} 
				else Invoke("ZombieRepath", Random.Range(0.1,0.5)); 
			}
		}
		else
		{
			 if(!IsInvoking("ZombieRepath")) Invoke("ZombieRepath", Random.Range(1,3));	
		}
	}
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////
function Update()
{
	if(type==0) DetermineHumanBehaviour(); else if(type==1) DetermineZombieBehaviour(); else {}
	if(path&&!stopmoving) followPath();
}

function OnPathComplete (p : Pathfinding.Path)
{
	if (!p.error) 
	{
		if(pathstorage) pathstorage = p; else path = p;
		currentWaypoint = 0;
	}
	else print("Error retrieving path:"+p.error);
} 